package tat;

import tat.domain.AllActorUseCaseRelations;
import tat.domain.AllActors;
import tat.domain.AllClasses;
import tat.domain.AllDomainModelRelations;
import tat.domain.AllUseCases;
import tat.domain.DomRequirement;
import tat.domain.DomainModelRelation;
import tat.domain.DomainModelRelationEndPoint;
import tat.domain.Relation;
import tat.domain.RelationFinder;
import tat.domain.RelationRequirement;
import tat.domain.Requirement;
import tat.domain.RequirementList;
import tat.domain.RequirementsReportBuilder;
import tat.domain.TatActor;
import tat.domain.TatClass;
import tat.domain.TatUseCase;
import tat.domain.TextRequirement;
import tat.domain.UcRequirement;
import tat.domain.UseCaseRelation;
import tat.domain.impl.AllActorUseCaseRelationsImpl;
import tat.domain.impl.AllActorsImpl;
import tat.domain.impl.AllClassesImpl;
import tat.domain.impl.AllDomainModelRelationsImpl;
import tat.domain.impl.AllUseCasesImpl;
import tat.exception.ObjectAlreadyExistsException;
import tat.factory.DomainModelRelationFactory;
import tat.factory.RequirementFactory;
import tat.factory.UseCaseRelationFactory;
import tat.factory.impl.FacImplRequirementFactory;
import tat.factory.impl.TextRequirementFactory;
import tat.mock.DomainModelRelationFactoryImpl;
import tat.mock.RelationFinderMock;
import tat.mock.UseCaseRelationFactoryMock;
import tat.util.Validate;

public class TatAppImpl implements TatApp {

	private AllClasses allClasses;

	private AllActors allActors;

	private AllUseCases allUseCases;

	private AllActorUseCaseRelations allActorUCRelations;

	private AllDomainModelRelations allDomainModelRelations;

	private RequirementList requirementList;

	public TatAppImpl() {
		// Implementações MOCKed
		this.allClasses = new AllClassesImpl();
		this.allActors = new AllActorsImpl();
		this.allUseCases = new AllUseCasesImpl();
		this.allActorUCRelations = new AllActorUseCaseRelationsImpl();
		this.allDomainModelRelations = new AllDomainModelRelationsImpl();
		this.requirementList = new RequirementList();
	}

	@Override
    public void createClassRelation(final String originName, final String destinationName,
            final String originMultiplicity, final String destinationMultiplicity,
            final boolean navigable, final String associationType, final boolean createRequirement) {

		TatClass origin = allClasses.find(originName);
		TatClass destination = allClasses.find(destinationName);

		Validate.notNull(origin, "TatClass originName not found.");
		Validate.notNull(destination, "TatClass destinationName not found.");

		DomainModelRelationEndPoint originEndPoint = new DomainModelRelationEndPoint(
				origin, originMultiplicity);
		origin.addObserver(originEndPoint);
		DomainModelRelationEndPoint destinationEndPoint = new DomainModelRelationEndPoint(
				destination, destinationMultiplicity);
		destination.addObserver(destinationEndPoint);
		DomainModelRelationFactory relationFactory = new DomainModelRelationFactoryImpl();
		DomainModelRelation relation = relationFactory.create(originEndPoint,
				destinationEndPoint, navigable, associationType);

		allDomainModelRelations.add(relation);

		if (createRequirement) {
		    createDomRequirement(relation.name());
		}
	}

	public DomainModelRelation[] conferDomainModelRelations() {
		return allDomainModelRelations.conferDomainModelRealtions();
	}

	@Override
	public void removeDomainModelRelation(final String relationName) {
		DomainModelRelation relation = allDomainModelRelations.find(relationName);

		if (requirementList.containsDerivedRequirement(relation)) {
			requirementList.removeDerivedRequirement(relation);
		}
		allDomainModelRelations.remove(relation);
	}

	@Override
	public void createUCActorRelation(final String actorName,
			final String ucName) {
		TatActor actor = allActors.find(actorName);
		TatUseCase useCase = allUseCases.find(ucName);
		UseCaseRelationFactory ucRelationFactory = new UseCaseRelationFactoryMock();
		UseCaseRelation relation = ucRelationFactory.create(actor, useCase);

		allActorUCRelations.add(relation);
	}

	@Override
	public void removeUCActorRelation(final String relationName) {
		UseCaseRelation useCaseRelation = allActorUCRelations
				.find(relationName);

		if (requirementList.containsDerivedRequirement(useCaseRelation)) {
			requirementList.removeDerivedRequirement(useCaseRelation);
		}
		allActorUCRelations.remove(useCaseRelation);
		useCaseRelation.actor().deleteObserver(useCaseRelation);
		useCaseRelation.useCase().deleteObserver(useCaseRelation);
	}

	@Override
	public void createRequirement(final String text, final int position) {
		RequirementFactory requirementFactory = new TextRequirementFactory();

		TextRequirement requirement = requirementFactory
				.requirement(text, position);

		requirementList.add(requirement);

	}


	public void createUcRequirement(final String relationName) {
	    UseCaseRelation relation = allActorUCRelations.find(relationName);

        FacImplRequirementFactory requirementFactory = new FacImplRequirementFactory();
        UcRequirement requirement = requirementFactory.ucRequirement(relation);

        requirementList.add(requirement);

	}

	public void createDomRequirement(final String relationName) {
	    DomainModelRelation relation = allDomainModelRelations.find(relationName);

    	FacImplRequirementFactory requirementFactory = new FacImplRequirementFactory();

    	DomRequirement requirement = requirementFactory.domRequirement(relation);

    	requirementList.add(requirement);


	}

	public void createRelationRequirement(final String relation) {
		FacImplRequirementFactory requirementFactory = new FacImplRequirementFactory();

		RelationRequirement requirement = requirementFactory
				.relationRequirement(relation);

		requirementList.add(requirement);


		}

	@Override
	public void createRequirement(final String relationType,
			final String relationName) {
		RelationFinder finder = new RelationFinderMock();

		Relation relation = finder.findRelation(relationType, relationName);

		Requirement requirement = relation.requirement();

		requirementList.add(requirement);
	}

	public void editRequirement(final String requirementText, final String newValue){
		Requirement req = requirementList.find(requirementText);
		req.changeValue(newValue);
		requirementList.remapRequirement(requirementText, req);
	}

	public void editClass(final String className, final String newValue){
		TatClass cls = allClasses.find(className);
		cls.changeName(newValue);
		allClasses.remapClass(className, cls);
	}

	@Override
	public void removeRequirement(final String requirementName) {
		Requirement requirement = requirementList.find(requirementName);
		requirementList.remove(requirement);
	}


	@Override
	public void createActor(final String actorName) {
		if (actorName == null || actorName.equals("")) {
			throw new IllegalArgumentException();
		}
		allActors.registerActor(actorName, new TatActor(actorName));
	}

	@Override
	public void removeActor(final String actorName) {
		TatActor tatActor = allActors.find(actorName);
		if(allActorUCRelations.containsDerivedRelations(tatActor)){
			UseCaseRelation[] relations = allActorUCRelations.identifyDerivedRelations(tatActor);
			for(UseCaseRelation rel : relations){
				this.removeUCActorRelation(rel.name());
			}
		}
		allActors.removeActor(actorName);
	}

	@Override
	public TatActor[] conferActors() {
		return allActors.conferActors();
	}

	@Override
	public String showModelAsText() {
		String text = "ACTORS\n";
		text = text + allActors.actorsAsText();
		text = text + "USE CASES\n";
		text = text + allUseCases.useCasesAsText();
		text = text + "USE CASE MODEL RELATIONS\n";
		text = text + allActorUCRelations.actorUseCaseRelationsAsText();
		text = text + "CLASSES\n";
		text = text + allClasses.classesAsText();
		text = text + "DOMAIN MODEL RELATIONS\n";
		text = text + allDomainModelRelations.domainModelRelationsAsText();

		return text;
	}

	@Override
	public String showModelAsXML() {
		// XML declaration
		String text = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
		// XML Document Type Definition
		text += "<!DOCTYPE TATDoc\n";
		text += "[<!ELEMENT TATDoc (actor|useCase|class)*>\n";
		text += "<!ELEMENT actor (#PCDATA)>\n";
		text += "<!ELEMENT useCase (#PCDATA)>\n";
		text += "<!ELEMENT class (attribute)*>\n";
		text += "<!ELEMENT attribute (#PCDATA)>\n]>\n\n<TATDoc>\n";
		text = text + allActors.actorsAsXML();
		text =  text + allUseCases.useCasesAsXML();
		text = text + allActorUCRelations.actorUseCaseRelationsAsXML();
		text = text + allClasses.classesAsXML();
		text = text + allDomainModelRelations.domainModelRelationsAsXML();
		
		text = text + "</TATDoc>";
		return text;

	}

	@Override
	public void resetApplication() {

		this.allClasses = new AllClassesImpl();
		this.allActors = new AllActorsImpl();
		this.allUseCases = new AllUseCasesImpl();
		this.allActorUCRelations = new AllActorUseCaseRelationsImpl();
		this.allDomainModelRelations = new AllDomainModelRelationsImpl();
		this.requirementList = new RequirementList();

	}

	@Override
	public Requirement[] conferRequirements() {
		return requirementList.conferRequirements();
	}

	@Override
	public TatClass[] conferClasses() {
		return allClasses.conferClasses();
	}

	@Override
	public void createClass(final String name) throws ObjectAlreadyExistsException {
		allClasses.registerClass(new TatClass(name));
	}

	@Override
	public void removeClass(final String name) {
		TatClass tatClass = allClasses.find(name);
		if(allDomainModelRelations.containsDerivedRelations(tatClass)){
			DomainModelRelation[] relations = allDomainModelRelations.identifyDerivedRelations(tatClass);
			for(DomainModelRelation rel : relations){
				this.removeDomainModelRelation(rel.name());
			}
		}
		allClasses.remove(name);
	}

	@Override
	public UseCaseRelation[] conferUCActorRelations() {
		return allActorUCRelations.confer();
	}

	@Override
	public void createUseCase(final String name) throws ObjectAlreadyExistsException {
		allUseCases.create(name);
	}

	@Override
	public void removeUseCase(final String name) {
		TatUseCase tatUseCase = allUseCases.find(name);
		if(allActorUCRelations.containsDerivedRelations(tatUseCase)){
			UseCaseRelation[] relations = allActorUCRelations.identifyDerivedRelations(tatUseCase);
			for(UseCaseRelation rel : relations){
				this.removeUCActorRelation(rel.name());
			}
		}
		allUseCases.remove(name);
	}

	@Override
	public TatUseCase[] conferUseCases() {
		return allUseCases.conferUseCases();
	}

	@Override
	public String showRequirementsReport() {
		RequirementsReportBuilder builder = new RequirementsReportBuilder();
		builder.defineReportData(requirementList.conferRequirements());
		builder.renderReport();
		return builder.exportReport();
	}

	@Override
	public void createAttribute(final String className, final String attName) throws ObjectAlreadyExistsException {
		TatClass c = allClasses.find(className);
		c.addAttribute(attName);
	}

	@Override
	public void editAttribute(final String className, final String oldAttributeName,
			final String newAttributeName) {
		TatClass tatClass = allClasses.find(className);

		tatClass.renameAttribute(oldAttributeName, newAttributeName);
	}

	@Override
	public void removeAttribute(final String className, final String attName) {
		TatClass tatClass = allClasses.find(className);

		tatClass.removeAttribute(attName);
	}
	@Override
	public void editActor(final String oldActorName, final String newActorName) {
		TatActor actor = allActors.find(oldActorName);
		actor.changeName(newActorName);
		allActors.remapActor(oldActorName, actor);
	}

	@Override
	public void editUseCase(final String oldUCName, final String newUCName) {
		TatUseCase useCase = allUseCases.find(oldUCName);
		useCase.changeName(newUCName);
		allUseCases.remapUseCase(oldUCName, useCase);

	}

}
